/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.siddhi.core.util.parser.helper;
import org.wso2.siddhi.core.config.ExecutionPlanContext;
import org.wso2.siddhi.core.event.stream.MetaStreamEvent;
import org.wso2.siddhi.core.event.stream.StreamEventCloner;
import org.wso2.siddhi.core.event.stream.StreamEventPool;
import org.wso2.siddhi.core.exception.ExecutionPlanCreationException;
import org.wso2.siddhi.core.function.Script;
import org.wso2.siddhi.core.stream.AttributeMapping;
import org.wso2.siddhi.core.stream.StreamJunction;
import org.wso2.siddhi.core.stream.input.source.Source;
import org.wso2.siddhi.core.stream.input.source.SourceMapper;
import org.wso2.siddhi.core.stream.output.sink.DynamicOptionGroupDeterminer;
import org.wso2.siddhi.core.stream.output.sink.OutputGroupDeterminer;
import org.wso2.siddhi.core.stream.output.sink.PartitionedGroupDeterminer;
import org.wso2.siddhi.core.stream.output.sink.Sink;
import org.wso2.siddhi.core.stream.output.sink.SinkMapper;
import org.wso2.siddhi.core.stream.output.sink.distributed.DistributedTransport;
import org.wso2.siddhi.core.stream.output.sink.distributed.DistributionStrategy;
import org.wso2.siddhi.core.table.InMemoryTable;
import org.wso2.siddhi.core.table.Table;
import org.wso2.siddhi.core.trigger.CronEventTrigger;
import org.wso2.siddhi.core.trigger.EventTrigger;
import org.wso2.siddhi.core.trigger.PeriodicEventTrigger;
import org.wso2.siddhi.core.trigger.StartEventTrigger;
import org.wso2.siddhi.core.util.SiddhiClassLoader;
import org.wso2.siddhi.core.util.SiddhiConstants;
import org.wso2.siddhi.core.util.config.ConfigReader;
import org.wso2.siddhi.core.util.extension.holder.DistributionStrategyExtensionHolder;
import org.wso2.siddhi.core.util.extension.holder.ScriptExtensionHolder;
import org.wso2.siddhi.core.util.extension.holder.SinkExecutorExtensionHolder;
import org.wso2.siddhi.core.util.extension.holder.SinkMapperExecutorExtensionHolder;
import org.wso2.siddhi.core.util.extension.holder.SourceExecutorExtensionHolder;
import org.wso2.siddhi.core.util.extension.holder.SourceMapperExecutorExtensionHolder;
import org.wso2.siddhi.core.util.extension.holder.TableExtensionHolder;
import org.wso2.siddhi.core.util.transport.MultiClientDistributedSink;
import org.wso2.siddhi.core.util.transport.Option;
import org.wso2.siddhi.core.util.transport.OptionHolder;
import org.wso2.siddhi.core.util.transport.SingleClientDistributedSink;
import org.wso2.siddhi.core.window.Window;
import org.wso2.siddhi.query.api.annotation.Annotation;
import org.wso2.siddhi.query.api.annotation.Element;
import org.wso2.siddhi.query.api.definition.AbstractDefinition;
import org.wso2.siddhi.query.api.definition.Attribute;
import org.wso2.siddhi.query.api.definition.FunctionDefinition;
import org.wso2.siddhi.query.api.definition.StreamDefinition;
import org.wso2.siddhi.query.api.definition.TableDefinition;
import org.wso2.siddhi.query.api.definition.TriggerDefinition;
import org.wso2.siddhi.query.api.definition.WindowDefinition;
import org.wso2.siddhi.query.api.exception.DuplicateDefinitionException;
import org.wso2.siddhi.query.api.exception.ExecutionPlanValidationException;
import org.wso2.siddhi.query.api.extension.Extension;
import org.wso2.siddhi.query.api.util.AnnotationHelper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Utility class for queryParser to help with QueryRuntime
* generation.
*/
public class DefinitionParserHelper {
public static void validateDefinition(AbstractDefinition definition, ConcurrentMap<String, AbstractDefinition>
streamDefinitionMap,
ConcurrentMap<String, AbstractDefinition> tableDefinitionMap,
ConcurrentMap<String, AbstractDefinition> windowDefinitionMap) {
AbstractDefinition existingTableDefinition = tableDefinitionMap.get(definition.getId());
if (existingTableDefinition != null && (!existingTableDefinition.equals(definition) || definition instanceof
StreamDefinition)) {
throw new DuplicateDefinitionException("Table Definition with same Stream Id '" +
definition.getId() + "' already exist : " + existingTableDefinition +
", hence cannot add " + definition);
}
AbstractDefinition existingStreamDefinition = streamDefinitionMap.get(definition.getId());
if (existingStreamDefinition != null && (!existingStreamDefinition.equals(definition) || definition
instanceof TableDefinition)) {
throw new DuplicateDefinitionException("Stream Definition with same Stream Id '" +
definition.getId() + "' already exist : " + existingStreamDefinition +
", hence cannot add " + definition);
}
AbstractDefinition existingWindowDefinition = windowDefinitionMap.get(definition.getId());
if (existingWindowDefinition != null && (!existingWindowDefinition.equals(definition) || definition
instanceof WindowDefinition)) {
throw new DuplicateDefinitionException("Window Definition with same Window Id '" +
definition.getId() + "' already exist : " + existingWindowDefinition +
", hence cannot add " + definition);
}
// TODO: 1/29/17 add source / sink both validation here
}
public static void addStreamJunction(StreamDefinition streamDefinition, ConcurrentMap<String, StreamJunction>
streamJunctionMap, ExecutionPlanContext executionPlanContext) {
if (!streamJunctionMap.containsKey(streamDefinition.getId())) {
StreamJunction streamJunction = new StreamJunction(streamDefinition,
executionPlanContext.getExecutorService(),
executionPlanContext.getBufferSize(), executionPlanContext);
streamJunctionMap.putIfAbsent(streamDefinition.getId(), streamJunction);
}
}
public static void validateOutputStream(StreamDefinition outputStreamDefinition, AbstractDefinition
existingStream) {
if (!existingStream.equalsIgnoreAnnotations(outputStreamDefinition)) {
throw new DuplicateDefinitionException("Different definition same as output stream definition :" +
outputStreamDefinition + " already exist as:" + existingStream);
}
}
public static void addTable(TableDefinition tableDefinition, ConcurrentMap<String, Table> tableMap,
ExecutionPlanContext executionPlanContext) {
if (!tableMap.containsKey(tableDefinition.getId())) {
MetaStreamEvent tableMetaStreamEvent = new MetaStreamEvent();
tableMetaStreamEvent.addInputDefinition(tableDefinition);
for (Attribute attribute : tableDefinition.getAttributeList()) {
tableMetaStreamEvent.addOutputData(attribute);
}
StreamEventPool tableStreamEventPool = new StreamEventPool(tableMetaStreamEvent, 10);
StreamEventCloner tableStreamEventCloner = new StreamEventCloner(tableMetaStreamEvent,
tableStreamEventPool);
Annotation annotation = AnnotationHelper.getAnnotation(SiddhiConstants.ANNOTATION_STORE,
tableDefinition.getAnnotations());
Table table;
ConfigReader configReader = null;
if (annotation != null) {
final String tableType = annotation.getElement(SiddhiConstants.ANNOTATION_ELEMENT_TYPE);
Extension extension = new Extension() {
@Override
public String getNamespace() {
return SiddhiConstants.NAMESPACE_STORE;
}
@Override
public String getName() {
return tableType;
}
};
table = (Table) SiddhiClassLoader.loadExtensionImplementation(
extension, TableExtensionHolder.getInstance(executionPlanContext));
configReader = executionPlanContext.getSiddhiContext().getConfigManager()
.generateConfigReader(extension.getNamespace(), extension.getName());
} else {
table = new InMemoryTable();
}
table.init(tableDefinition, tableStreamEventPool, tableStreamEventCloner, configReader,
executionPlanContext);
tableMap.putIfAbsent(tableDefinition.getId(), table);
}
}
public static void addWindow(WindowDefinition windowDefinition, ConcurrentMap<String, Window>
eventWindowMap,
ExecutionPlanContext executionPlanContext) {
if (!eventWindowMap.containsKey(windowDefinition.getId())) {
Window table = new Window(windowDefinition, executionPlanContext);
eventWindowMap.putIfAbsent(windowDefinition.getId(), table);
}
}
public static void addFunction(ExecutionPlanContext executionPlanContext, final FunctionDefinition
functionDefinition) {
Extension extension = new Extension() {
@Override
public String getNamespace() {
return "script";
}
@Override
public String getName() {
return functionDefinition.getLanguage().toLowerCase();
}
};
Script script = (Script) SiddhiClassLoader.loadExtensionImplementation(extension,
ScriptExtensionHolder.getInstance(executionPlanContext));
ConfigReader configReader = executionPlanContext.getSiddhiContext()
.getConfigManager().generateConfigReader(extension
.getNamespace(), extension.getName());
script.setReturnType(functionDefinition.getReturnType());
script.init(functionDefinition.getId(), functionDefinition.getBody(), configReader);
executionPlanContext.getScriptFunctionMap().put(functionDefinition.getId(), script);
}
public static void validateDefinition(TriggerDefinition triggerDefinition) {
if (triggerDefinition.getId() != null) {
if (triggerDefinition.getAtEvery() == null) {
String expression = triggerDefinition.getAt();
if (expression == null) {
throw new ExecutionPlanValidationException("Trigger Definition '" + triggerDefinition.getId() +
"' must have trigger time defined");
} else {
if (!expression.trim().equalsIgnoreCase(SiddhiConstants.TRIGGER_START)) {
try {
org.quartz.CronExpression.isValidExpression(expression);
} catch (Throwable t) {
throw new ExecutionPlanValidationException("Trigger Definition '" + triggerDefinition
.getId() +
"' have invalid trigger time defined, expected 'start' or valid cron but found '"
+ expression + "'");
}
}
}
} else if (triggerDefinition.getAt() != null) {
throw new ExecutionPlanValidationException("Trigger Definition '" + triggerDefinition.getId() + "' " +
"must either have trigger time in cron or 'start' or time interval defined, and it cannot " +
"have more than one defined as '" + triggerDefinition + "'");
}
} else {
throw new ExecutionPlanValidationException("Trigger Definition id cannot be null");
}
}
public static void addEventTrigger(TriggerDefinition triggerDefinition,
ConcurrentMap<String, EventTrigger> eventTriggerMap,
ConcurrentMap<String, StreamJunction> streamJunctionMap,
ExecutionPlanContext executionPlanContext) {
if (!eventTriggerMap.containsKey(triggerDefinition.getId())) {
EventTrigger eventTrigger;
if (triggerDefinition.getAtEvery() != null) {
eventTrigger = new PeriodicEventTrigger();
} else if (triggerDefinition.getAt().trim().equalsIgnoreCase(SiddhiConstants.TRIGGER_START)) {
eventTrigger = new StartEventTrigger();
} else {
eventTrigger = new CronEventTrigger();
}
StreamJunction streamJunction = streamJunctionMap.get(triggerDefinition.getId());
eventTrigger.init(triggerDefinition, executionPlanContext, streamJunction);
executionPlanContext.addEternalReferencedHolder(eventTrigger);
eventTriggerMap.putIfAbsent(eventTrigger.getId(), eventTrigger);
}
}
public static void addEventSource(StreamDefinition streamDefinition,
ConcurrentMap<String, List<Source>> eventSourceMap,
ExecutionPlanContext executionPlanContext) {
for (Annotation sourceAnnotation : streamDefinition.getAnnotations()) {
if (SiddhiConstants.ANNOTATION_SOURCE.equalsIgnoreCase(sourceAnnotation.getName())) {
Annotation mapAnnotation = AnnotationHelper.getAnnotation(SiddhiConstants.ANNOTATION_MAP,
sourceAnnotation.getAnnotations());
if (mapAnnotation == null) {
mapAnnotation = Annotation.annotation(SiddhiConstants.ANNOTATION_MAP).element(SiddhiConstants
.ANNOTATION_ELEMENT_TYPE, "passThrough");
}
final String sourceType = sourceAnnotation.getElement(SiddhiConstants.ANNOTATION_ELEMENT_TYPE);
final String mapType = mapAnnotation.getElement(SiddhiConstants.ANNOTATION_ELEMENT_TYPE);
if (sourceType != null && mapType != null) {
// load input transport extension
Extension sourceExtension = constructExtension(streamDefinition, SiddhiConstants.ANNOTATION_SOURCE,
sourceType, sourceAnnotation, SiddhiConstants.NAMESPACE_SOURCE);
Source source = (Source) SiddhiClassLoader.loadExtensionImplementation(
sourceExtension, SourceExecutorExtensionHolder.getInstance(executionPlanContext));
// load input mapper extension
Extension mapperExtension = constructExtension(streamDefinition, SiddhiConstants.ANNOTATION_MAP,
mapType, sourceAnnotation, SiddhiConstants.NAMESPACE_SOURCE_MAPPER);
SourceMapper sourceMapper = (SourceMapper) SiddhiClassLoader.loadExtensionImplementation(
mapperExtension, SourceMapperExecutorExtensionHolder.getInstance(executionPlanContext));
OptionHolder sourceOptionHolder = constructOptionProcessor(streamDefinition, sourceAnnotation,
source.getClass().getAnnotation(org.wso2.siddhi.annotation.Extension.class), null);
OptionHolder mapOptionHolder = constructOptionProcessor(streamDefinition, mapAnnotation,
sourceMapper.getClass().getAnnotation(org.wso2.siddhi.annotation.Extension.class), null);
sourceMapper.init(streamDefinition, mapType, mapOptionHolder, getAttributeMappings(mapAnnotation),
executionPlanContext.getSiddhiContext().getConfigManager().generateConfigReader
(mapperExtension.getNamespace(), mapperExtension.getName()));
source.init(sourceOptionHolder, sourceMapper, executionPlanContext.getSiddhiContext()
.getConfigManager().generateConfigReader
(sourceExtension.getNamespace(), sourceExtension.getName()), executionPlanContext);
List<Source> eventSources = eventSourceMap.get(streamDefinition.getId());
if (eventSources == null) {
eventSources = new ArrayList<>();
eventSources.add(source);
eventSourceMap.put(streamDefinition.getId(), eventSources);
} else {
eventSources.add(source);
}
} else {
throw new ExecutionPlanCreationException("Both @Sink(type=) and @map(type=) are required.");
}
}
}
}
public static void addEventSink(StreamDefinition streamDefinition,
ConcurrentMap<String, List<Sink>> eventSinkMap,
ExecutionPlanContext executionPlanContext) {
for (Annotation sinkAnnotation : streamDefinition.getAnnotations()) {
if (SiddhiConstants.ANNOTATION_SINK.equalsIgnoreCase(sinkAnnotation.getName())) {
Annotation mapAnnotation = AnnotationHelper.getAnnotation(SiddhiConstants.ANNOTATION_MAP,
sinkAnnotation.getAnnotations());
if (mapAnnotation == null) {
mapAnnotation = Annotation.annotation(SiddhiConstants.ANNOTATION_MAP).element(SiddhiConstants
.ANNOTATION_ELEMENT_TYPE, "passThrough");
}
Annotation distributionAnnotation =
AnnotationHelper.getAnnotation(SiddhiConstants.ANNOTATION_DISTRIBUTION,
sinkAnnotation.getAnnotations());
if (mapAnnotation != null) {
String[] supportedDynamicOptions = null;
List<OptionHolder> destinationOptHolders = new ArrayList<>();
String sinkType = sinkAnnotation.getElement(SiddhiConstants.ANNOTATION_ELEMENT_TYPE);
Extension sinkExtension = constructExtension(streamDefinition, SiddhiConstants.ANNOTATION_SINK,
sinkType, sinkAnnotation, SiddhiConstants.NAMESPACE_SINK);
ConfigReader sinkConfigReader = executionPlanContext.getSiddhiContext().
getConfigManager().generateConfigReader(sinkExtension.getNamespace(),
sinkExtension.getName());
final boolean isDistributedTransport = (distributionAnnotation != null);
boolean isMultiClient = false;
if (isDistributedTransport) {
Sink sink = createSink(sinkExtension, executionPlanContext);
isMultiClient = isMultiClientDistributedTransport(sink, streamDefinition,
distributionAnnotation);
supportedDynamicOptions = sink.getSupportedDynamicOptions();
destinationOptHolders = createDestinationOptionHolders(distributionAnnotation,
streamDefinition, sink);
}
final String mapType = mapAnnotation.getElement(SiddhiConstants.ANNOTATION_ELEMENT_TYPE);
if (mapType != null) {
Sink sink;
if (isDistributedTransport) {
sink = (isMultiClient) ? new MultiClientDistributedSink() :
new SingleClientDistributedSink();
} else {
sink = createSink(sinkExtension, executionPlanContext);
}
if (supportedDynamicOptions == null) {
supportedDynamicOptions = sink.getSupportedDynamicOptions();
}
//load output mapper extension
Extension mapperExtension = constructExtension(streamDefinition, SiddhiConstants.ANNOTATION_MAP,
mapType, sinkAnnotation, SiddhiConstants.NAMESPACE_SINK_MAPPER);
ConfigReader mapperConfigReader = executionPlanContext.getSiddhiContext().
getConfigManager().generateConfigReader(sinkExtension.getNamespace(),
sinkExtension.getName());
SinkMapper sinkMapper = (SinkMapper) SiddhiClassLoader.loadExtensionImplementation(
mapperExtension, SinkMapperExecutorExtensionHolder.getInstance(executionPlanContext));
org.wso2.siddhi.annotation.Extension sinkExt
= sink.getClass().getAnnotation(org.wso2.siddhi.annotation.Extension.class);
// Initialing output transport
OptionHolder transportOptionHolder = constructOptionProcessor(streamDefinition,
sinkAnnotation, sinkExt, supportedDynamicOptions);
OptionHolder mapOptionHolder = constructOptionProcessor(streamDefinition, mapAnnotation,
sinkMapper.getClass().getAnnotation(org.wso2.siddhi.annotation.Extension.class),
sinkMapper.getSupportedDynamicOptions());
String payload = getPayload(mapAnnotation);
// Initializing the transports
OptionHolder distributionOptHolder = null;
if (isDistributedTransport) {
distributionOptHolder = constructOptionProcessor(streamDefinition,
distributionAnnotation, sinkExt, supportedDynamicOptions);
String strategyType = distributionOptHolder.validateAndGetStaticValue(SiddhiConstants
.DISTRIBUTION_STRATEGY_KEY);
Extension strategyExtension = constructExtension(streamDefinition, SiddhiConstants
.ANNOTATION_SINK,
strategyType, sinkAnnotation, SiddhiConstants
.NAMESPACE_DISTRIBUTION_STRATEGY);
ConfigReader configReader = executionPlanContext.getSiddhiContext().
getConfigManager().generateConfigReader
(strategyExtension.getNamespace(), strategyExtension.getName());
DistributionStrategy distributionStrategy = (DistributionStrategy) SiddhiClassLoader
.loadExtensionImplementation(
strategyExtension, DistributionStrategyExtensionHolder.getInstance
(executionPlanContext));
distributionStrategy.init(streamDefinition, transportOptionHolder, distributionOptHolder,
destinationOptHolders, configReader);
((DistributedTransport) sink).init(streamDefinition, sinkType,
transportOptionHolder, sinkConfigReader, sinkMapper,
mapType, mapOptionHolder,
payload, mapperConfigReader, executionPlanContext,
destinationOptHolders,
sinkAnnotation, distributionStrategy,
supportedDynamicOptions);
} else {
sink.init(streamDefinition, sinkType, transportOptionHolder, sinkConfigReader, sinkMapper,
mapType, mapOptionHolder, payload, mapperConfigReader, executionPlanContext);
}
// Setting the output group determiner
OutputGroupDeterminer groupDeterminer = constructOutputGroupDeterminer(transportOptionHolder,
distributionOptHolder, streamDefinition, destinationOptHolders.size());
if (groupDeterminer != null) {
sink.getMapper().setGroupDeterminer(groupDeterminer);
}
List<Sink> eventSinks = eventSinkMap.get(streamDefinition.getId());
if (eventSinks == null) {
eventSinks = new ArrayList<>();
eventSinks.add(sink);
eventSinkMap.put(streamDefinition.getId(), eventSinks);
} else {
eventSinks.add(sink);
}
}
} else {
throw new ExecutionPlanCreationException("Both @sink(type=) and @map(type=) are required.");
}
}
}
}
private static OutputGroupDeterminer constructOutputGroupDeterminer(OptionHolder transportOptHolder, OptionHolder
distributedOptHolder,
StreamDefinition streamDef, int
destinationCount) {
OutputGroupDeterminer groupDeterminer = null;
if (distributedOptHolder != null) {
String strategy = distributedOptHolder.validateAndGetStaticValue(SiddhiConstants.DISTRIBUTION_STRATEGY_KEY);
if (strategy.equalsIgnoreCase(SiddhiConstants.DISTRIBUTION_STRATEGY_PARTITIONED)) {
String partitionKeyField = distributedOptHolder.validateAndGetStaticValue(SiddhiConstants
.PARTITION_KEY_FIELD_KEY);
int partitioningFieldIndex = streamDef.getAttributePosition(partitionKeyField);
groupDeterminer = new PartitionedGroupDeterminer(partitioningFieldIndex, destinationCount);
}
}
if (groupDeterminer == null) {
List<Option> dynamicTransportOptions = new ArrayList<Option>(transportOptHolder.getDynamicOptionsKeys()
.size());
transportOptHolder.getDynamicOptionsKeys().
forEach(option -> dynamicTransportOptions.add(transportOptHolder.validateAndGetOption(option)));
if (dynamicTransportOptions.size() > 0) {
groupDeterminer = new DynamicOptionGroupDeterminer(dynamicTransportOptions);
}
}
return groupDeterminer;
}
public static Extension constructExtension(StreamDefinition streamDefinition, String typeName, String typeValue,
Annotation annotation, String defaultNamespace) {
String[] namespaceAndName = typeValue.split(SiddhiConstants.EXTENSION_SEPARATOR);
String namespace;
String name;
if (namespaceAndName.length == 1) {
namespace = defaultNamespace;
name = namespaceAndName[0];
} else if (namespaceAndName.length == 2) {
namespace = namespaceAndName[0];
name = namespaceAndName[1];
} else {
throw new ExecutionPlanCreationException("Malformed '" + typeName + "' annotation type '" + typeValue +
"' " +
"provided, for annotation '" + annotation + "' on stream '" + streamDefinition.getId() + "', " +
"it should be either '<namespace>:<name>' or '<name>'");
}
return new Extension() {
@Override
public String getNamespace() {
return namespace;
}
@Override
public String getName() {
return name;
}
};
}
private static List<AttributeMapping> getAttributeMappings(Annotation mapAnnotation) {
List<AttributeMapping> mappings = new ArrayList<>();
List<Annotation> attributeAnnotations = mapAnnotation.getAnnotations(SiddhiConstants.ANNOTATION_ATTRIBUTES);
if (attributeAnnotations.size() > 0) {
mappings.addAll(
attributeAnnotations
.get(0)
.getElements()
.stream()
.map(element -> new AttributeMapping(element.getKey(), element.getValue()))
.collect(Collectors.toList())
);
}
return mappings;
}
private static String getPayload(Annotation mapAnnotation) {
List<Annotation> attributeAnnotations = mapAnnotation.getAnnotations(SiddhiConstants.ANNOTATION_PAYLOAD);
if (attributeAnnotations.size() == 1) {
List<Element> elements = attributeAnnotations.get(0).getElements();
if (elements.size() == 1) {
return elements.get(0).getValue();
} else {
throw new ExecutionPlanCreationException("@payload() annotation should only contain single element.");
}
} else if (attributeAnnotations.size() > 1) {
throw new ExecutionPlanCreationException("@map() annotation should only contain single @payload() " +
"annotation.");
} else {
return null;
}
}
private static OptionHolder constructOptionProcessor(StreamDefinition streamDefinition, Annotation annotation,
org.wso2.siddhi.annotation.Extension extension,
String[] supportedDynamicOptions) {
List<String> supportedDynamicOptionList = new ArrayList<>();
if (supportedDynamicOptions != null) {
supportedDynamicOptionList = Arrays.asList(supportedDynamicOptions);
}
Map<String, String> options = new HashMap<String, String>();
Map<String, String> dynamicOptions = new HashMap<String, String>();
for (Element element : annotation.getElements()) {
if (Pattern.matches("\\{\\{.*?}}", element.getValue())) {
if (supportedDynamicOptionList.contains(element.getKey())) {
dynamicOptions.put(element.getKey(), element.getValue());
} else {
throw new ExecutionPlanCreationException("'" + element.getKey() + "' is not a supported " +
"DynamicOption " +
"for the Extension '" + extension.namespace() + ":" + extension.name() + "', it only " +
"supports " +
"following as its DynamicOptions: " + supportedDynamicOptionList);
}
} else {
options.put(element.getKey(), element.getValue());
}
}
return new OptionHolder(streamDefinition, options, dynamicOptions, extension);
}
private static boolean isMultiClientDistributedTransport(Sink clientTransport, StreamDefinition
streamDefinition, Annotation distributionAnnotation) {
// fetch the @distribution annotations from the @sink annotation
List<OptionHolder> destinationOptHolders = createDestinationOptionHolders(distributionAnnotation,
streamDefinition, clientTransport);
List<String> dynamicOptions = Arrays.asList(clientTransport.getSupportedDynamicOptions());
// If at least one of the @destination contains a static option then multi client transport should be used
for (OptionHolder optionHolder : destinationOptHolders) {
for (String key : optionHolder.getDynamicOptionsKeys()) {
if (!dynamicOptions.contains(key)) {
return true;
}
}
for (String key : optionHolder.getStaticOptionsKeys()) {
if (!dynamicOptions.contains(key)) {
return true;
}
}
}
return false;
}
private static Sink createSink(Extension sinkExtension, ExecutionPlanContext executionPlanContext) {
// Create a temp instance of the underlying transport to get supported dynamic options
Sink sink = (Sink) SiddhiClassLoader.loadExtensionImplementation(
sinkExtension, SinkExecutorExtensionHolder.getInstance(executionPlanContext));
return sink;
}
private static List<OptionHolder> createDestinationOptionHolders(Annotation distributionAnnotation, StreamDefinition
streamDefinition, Sink clientTransport) {
org.wso2.siddhi.annotation.Extension sinkExt
= clientTransport.getClass().getAnnotation(org.wso2.siddhi.annotation.Extension.class);
List<OptionHolder> destinationOptHolders = new ArrayList<>();
distributionAnnotation.getAnnotations().stream()
.filter(annotation -> annotation.getName().equalsIgnoreCase(SiddhiConstants.ANNOTATION_DESTINATION))
.forEach(destinationAnnotation -> destinationOptHolders.add(constructOptionProcessor(streamDefinition,
destinationAnnotation, sinkExt, clientTransport.getSupportedDynamicOptions())));
return destinationOptHolders;
}
}